#include "elcheckbox.h"
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPropertyAnimation>
#include "../basic/eltheme.h"

ElCheckbox::ElCheckbox(QWidget *parent)
    : ElAbstractButton{parent}
{
    _box = QRectF(10, 8, 15, 15);
    initPalette();
}

ElCheckbox::ElCheckbox(const QString text, QWidget *parent)
    : ElCheckbox{parent}
{
    setText(text);
}

Qt::CheckState ElCheckbox::checkState() const
{
    return _checkState;
}

void ElCheckbox::setCheckState(Qt::CheckState checkState)
{
    if (_checkState == checkState)
        return;
    _checkState = checkState;
    emit checkStateChanged(_checkState);
    update();
}

bool ElCheckbox::isTristate() const
{
    return _tristate;
}

void ElCheckbox::setTristate(bool tristate)
{
    _tristate = tristate;
}

bool ElCheckbox::hasBorder() const
{
    return _border;
}

void ElCheckbox::setBorder(bool b)
{
    _border = b;
    update();
}

bool ElCheckbox::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        _palette.toggleColorGroup(isEnabled() ? ElPalette::Normal : ElPalette::Disabled);
        update();
    }
    return ElAbstractButton::event(e);
}

void ElCheckbox::paintEvent(QPaintEvent *e)
{
    bool isChecked = (_checkState == Qt::Checked);
    QPainter painter(this);
    if (_border) // draw outer border
        drawBorder(&painter);
    painter.setPen(_palette.color(ElPalette::Border));
    painter.setBrush(_palette.color(ElPalette::Background));
    painter.drawRect(_box);
    if (isChecked) {
        drawChecked(&painter);
    } else if (_checkState == Qt::PartiallyChecked) {
        drawPartiallyChecked(&painter);
    }
    QColor fgColor = ThemeColor::primaryColor;
    if (isChecked) {
        if (!isEnabled())
            fgColor = ThemeColor::primaryDisabledColor;
    } else {
        fgColor = _palette.color(ElPalette::Foreground);
    }
    painter.setPen(fgColor);
    QRectF drawingRect{_box.right() + 5, _box.top(), 10, _box.height()}, br;
    painter.drawText(drawingRect, Qt::AlignVCenter | Qt::AlignLeft, _text, &br);
    resize(_box.left() + br.right(), height());
    // painter.eraseRect(drawingRect);
    drawingRect = QRectF{_box.right() + 5, _box.top(), br.width(), _box.height()};
    painter.drawText(drawingRect, Qt::AlignVCenter | Qt::AlignLeft, _text);
    e->accept();
}

void ElCheckbox::enterEvent(QEnterEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    showTooltip();
    _palette.toggleColorGroup(ElPalette::Entered);
    update();
    e->accept();
}

void ElCheckbox::leaveEvent(QEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    hideTooltip();
    _palette.toggleColorGroup(ElPalette::Normal);
    update();
    e->accept();
}

void ElCheckbox::mousePressEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Pressed);
    update();
    emit pressed();
    e->accept();
}

void ElCheckbox::mouseReleaseEvent(QMouseEvent *e)
{
    if (!isEnabled()) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Entered);
    emit released();
    auto pos = e->pos();
    if (pos.x() >= 0 && pos.y() >= 0 && pos.x() <= width() && pos.y() <= height()) {
        emit clicked();
        int offset = _tristate ? 1 : 2;
        int state = _checkState;
        state += offset;
        if (state > 2)
            state = 0;
        _checkState = Qt::CheckState(state);
        emit checkStateChanged(_checkState);
    }
    update();
    e->accept();
}

void ElCheckbox::initPalette()
{
    // background is default color, which is white.
    _palette.setColor(ElPalette::Border, ThemeColor::lineColor);
    _palette.setColor(ElPalette::Foreground, ThemeColor::textPrimaryColor);

    _palette.toggleColorGroup(ElPalette::Entered);
    _palette.setColor(ElPalette::Border, ThemeColor::lineColor);
    _palette.setColor(ElPalette::Foreground, ThemeColor::textPrimaryColor);

    _palette.toggleColorGroup(ElPalette::Pressed);
    _palette.setColor(ElPalette::Background, ThemeColor::primaryShallowColor);
    _palette.setColor(ElPalette::Border, ThemeColor::primaryColor);
    _palette.setColor(ElPalette::Foreground, ThemeColor::textPrimaryColor);

    _palette.toggleColorGroup(ElPalette::Disabled);
    _palette.setColor(ElPalette::Border, ThemeColor::lineDisabledColor);
    _palette.setColor(ElPalette::Foreground, ThemeColor::textDisabledColor);
    _palette.toggleColorGroup(ElPalette::Normal);
}

void ElCheckbox::drawBorder(QPainter *painter)
{
    QPen pen;
    bool isChecked = (_checkState == Qt::Checked);
    if (isChecked) {
        pen.setWidth(1);
        pen.setColor(isEnabled() ? ThemeColor::primaryColor : ThemeColor::primaryDisabledColor);
    } else {
        pen.setColor(isEnabled() ? ThemeColor::lineColor : ThemeColor::lineDisabledColor);
    }
    pen.setJoinStyle(Qt::RoundJoin);
    pen.setCapStyle(Qt::RoundCap);
    painter->setPen(pen);
    painter->setBrush(Qt::white);
    painter->drawRoundedRect(getDrawRect(), height() / 5, height() / 5);
}

void ElCheckbox::drawChecked(QPainter *painter)
{
    QColor fillColor = ThemeColor::primaryColor;
    if (!isEnabled())
        fillColor = ThemeColor::primaryDisabledColor;
    painter->fillRect(_box, fillColor);
    QPen pen(QBrush(Qt::white), 1);
    painter->setPen(pen);
    auto x = 4.0 + _box.x(), y = _box.height() / 2.0 + _box.y();
    auto y2 = _box.height() * 2 / 3.0 + _box.y();
    auto x2 = x + y2 - y;
    painter->drawLine(x, y, x2, y2);
    auto y3 = _box.height() / 3.0 + _box.y();
    auto x3 = x2 - (y3 - y2);
    painter->drawLine(x2, y2, x3, y3);
}

void ElCheckbox::drawPartiallyChecked(QPainter *painter)
{
    QColor fillColor = ThemeColor::primaryColor;
    if (!isEnabled())
        fillColor = ThemeColor::primaryDisabledColor;
    painter->fillRect(_box, fillColor);
    QPen pen(QBrush(Qt::white), 1);
    painter->setPen(pen);
    qreal offset = 4;
    painter->drawLine(offset + _box.left(),
                      _box.height() / 2 + _box.top(),
                      _box.right() - offset,
                      _box.height() / 2 + _box.top());
}
